#include "ReedSolomonEncoder.h"
#include <zxing/common/IllegalArgumentException.h>
#include <cstring>

namespace zxing{
using namespace std;

ReedSolomonEncoder::ReedSolomonEncoder(GF256 &field):
    field(field)
{

    this->cachedGenerators = vector<Ref<GF256Poly> >();
    int poly[] = {1};
    cachedGenerators.push_back(Ref<GF256Poly>(
        new GF256Poly(field, ArrayRef<int>(poly, 1))));
}

Ref<GF256Poly> ReedSolomonEncoder::buildGenerator(int degree)
{
    if (degree >= (int)cachedGenerators.size())
    {
        Ref<GF256Poly> lastGenerator =
            cachedGenerators[cachedGenerators.size() - 1];
        for (int d = cachedGenerators.size(); d <= degree; d++)
        {
            int coefficients[] = {1, field.exp(d - 1)};
            Ref<GF256Poly> nextGenerator = lastGenerator->multiply(
                Ref<GF256Poly>(new GF256Poly(field,
                ArrayRef<int>(coefficients, 2))));
            cachedGenerators.push_back(nextGenerator);
            lastGenerator = nextGenerator;
        }

    }
    return cachedGenerators[degree];
}

void ReedSolomonEncoder::encode(ArrayRef<int> toEncode, int ecBytes)
{
    if (ecBytes == 0)
    {
        throw IllegalArgumentException("No error correction bytes");
    }
    int dataBytes = toEncode.size() - ecBytes;
    if (dataBytes <= 0)
    {
        throw IllegalArgumentException("No data bytes provided");
    }
    Ref<GF256Poly> generator = buildGenerator(ecBytes);
    ArrayRef<int> infoCoefficients = ArrayRef<int>(
                new int[dataBytes], dataBytes);
    memcpy(&infoCoefficients[0], &toEncode[0], dataBytes * sizeof(int));
    Ref<GF256Poly> info = Ref<GF256Poly>(
                new GF256Poly(field, infoCoefficients));
    info = info->multiplyByMonomial(ecBytes, 1);
    GF256Poly remainder = info->divide(generator)[1];
    ArrayRef<int> coefficients = remainder.getCoefficients();
    int numZeroCoefficients = ecBytes - coefficients.size();
    for (int i = 0; i < numZeroCoefficients; i++) {
      toEncode[dataBytes + i] = 0;
    }
    memcpy(&toEncode[dataBytes + numZeroCoefficients], &coefficients[0],
           coefficients.size() * sizeof(int));
}

}
